package grape.workflow.rest.flowable.ext;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import grape.workflow.rest.task.vo.TaskRepresentationVo;
import org.apache.commons.lang3.StringUtils;
import org.flowable.app.api.repository.AppDefinition;
import org.flowable.cmmn.api.repository.CaseDefinition;
import org.flowable.cmmn.api.repository.CmmnDeployment;
import org.flowable.editor.language.json.converter.util.CollectionUtils;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.repository.Deployment;
import org.flowable.idm.api.User;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.TaskInfoQueryWrapper;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.ui.common.model.ResultListDataRepresentation;
import org.flowable.ui.common.model.UserRepresentation;
import org.flowable.ui.common.security.SecurityUtils;
import org.flowable.ui.common.service.exception.BadRequestException;
import org.flowable.ui.task.model.runtime.TaskRepresentation;
import org.flowable.ui.task.service.api.UserCache;
import org.flowable.ui.task.service.runtime.FlowableTaskQueryService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by yangwei
 * Created at 2020/6/17 13:25
 */
@Service
public class CustomFlowableTaskQueryService extends FlowableTaskQueryService {

    private static final int DEFAULT_PAGE_SIZE = 25;

    public ResultListDataRepresentation listTasks(ObjectNode requestNode) {

        if (requestNode == null) {
            throw new BadRequestException("No request found");
        }
        User currentUser = SecurityUtils.getCurrentUserObject();

        JsonNode stateNode = requestNode.get("state");
        TaskInfoQueryWrapper taskInfoQueryWrapper = null;
        if (stateNode != null && "completed".equals(stateNode.asText())) {
            HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery();
            historicTaskInstanceQuery.finished();
            taskInfoQueryWrapper = new TaskInfoQueryWrapper(historicTaskInstanceQuery);
        } else {
            taskInfoQueryWrapper = new TaskInfoQueryWrapper(taskService.createTaskQuery());
        }

        JsonNode appDefinitionKeyNode = requestNode.get("appDefinitionKey");
        if (appDefinitionKeyNode != null && !appDefinitionKeyNode.isNull()) {
            List<AppDefinition> appDefinitions = appRepositoryService.createAppDefinitionQuery().appDefinitionKey(appDefinitionKeyNode.asText()).list();
            List<String> parentDeploymentIds = new ArrayList<>();
            for (AppDefinition appDefinition : appDefinitions) {
                parentDeploymentIds.add(appDefinition.getDeploymentId());
            }

            List<Deployment> deployments = repositoryService.createDeploymentQuery().parentDeploymentIds(parentDeploymentIds).list();
            List<String> deploymentIds = new ArrayList<>();
            for (Deployment deployment : deployments) {
                deploymentIds.add(deployment.getId());
            }

            List<CmmnDeployment> cmmnDeployments = cmmnRepositoryService.createDeploymentQuery().parentDeploymentIds(parentDeploymentIds).list();
            List<String> cmmnDeploymentIds = new ArrayList<>();
            for (CmmnDeployment deployment : cmmnDeployments) {
                cmmnDeploymentIds.add(deployment.getId());
            }

            taskInfoQueryWrapper.getTaskInfoQuery().or()
                    .deploymentIdIn(deploymentIds)
                    .cmmnDeploymentIdIn(cmmnDeploymentIds)
                    .taskCategory(appDefinitionKeyNode.asText())
                    .endOr();
        }

        JsonNode processInstanceIdNode = requestNode.get("processInstanceId");
        if (processInstanceIdNode != null && !processInstanceIdNode.isNull()) {
            handleProcessInstanceFiltering(currentUser, taskInfoQueryWrapper, processInstanceIdNode);
        }

        JsonNode caseInstanceIdNode = requestNode.get("caseInstanceId");
        if (caseInstanceIdNode != null && !caseInstanceIdNode.isNull()) {
            handleCaseInstanceFiltering(currentUser, taskInfoQueryWrapper, caseInstanceIdNode);
        }

        JsonNode textNode = requestNode.get("text");
        if (textNode != null && !textNode.isNull()) {
            handleTextFiltering(taskInfoQueryWrapper, textNode);
        }

        JsonNode assignmentNode = requestNode.get("assignment");
        if (assignmentNode != null && !assignmentNode.isNull()) {
            handleAssignment(taskInfoQueryWrapper, assignmentNode, currentUser);
        }

        JsonNode processDefinitionNode = requestNode.get("processDefinitionId");
        if (processDefinitionNode != null && !processDefinitionNode.isNull()) {
            handleProcessDefinition(taskInfoQueryWrapper, processDefinitionNode);
        }

        JsonNode dueBeforeNode = requestNode.get("dueBefore");
        if (dueBeforeNode != null && !dueBeforeNode.isNull()) {
            handleDueBefore(taskInfoQueryWrapper, dueBeforeNode);
        }

        JsonNode dueAfterNode = requestNode.get("dueAfter");
        if (dueAfterNode != null && !dueAfterNode.isNull()) {
            handleDueAfter(taskInfoQueryWrapper, dueAfterNode);
        }

        JsonNode sortNode = requestNode.get("sort");
        if (sortNode != null) {
            handleSorting(taskInfoQueryWrapper, sortNode);
        }else {
            taskInfoQueryWrapper.getTaskInfoQuery().orderByTaskCreateTime().desc();
        }

        int page = 0;
        JsonNode pageNode = requestNode.get("page");
        if (pageNode != null && !pageNode.isNull()) {
            page = pageNode.asInt(0);
        }

        int size = DEFAULT_PAGE_SIZE;
        JsonNode sizeNode = requestNode.get("size");
        if (sizeNode != null && !sizeNode.isNull()) {
            size = sizeNode.asInt(DEFAULT_PAGE_SIZE);
        }

        // 只修改了这里
        handleExt(taskInfoQueryWrapper, requestNode);
        List<? extends TaskInfo> tasks = taskInfoQueryWrapper.getTaskInfoQuery().listPage(page * size, size);

        JsonNode includeProcessInstanceNode = requestNode.get("includeProcessInstance");
        Map<String, String> processInstancesNames = new HashMap<>();
        Map<String, String> caseInstancesNames = new HashMap<>();
        if (includeProcessInstanceNode != null) {
            handleIncludeProcessInstance(taskInfoQueryWrapper, includeProcessInstanceNode, tasks, processInstancesNames);
            handleIncludeCaseInstance(taskInfoQueryWrapper, includeProcessInstanceNode, tasks, caseInstancesNames);
        }

        ResultListDataRepresentation result = new ResultListDataRepresentation(convertTaskInfoList(tasks, processInstancesNames, caseInstancesNames));

        // In case we're not on the first page and the size exceeds the page size, we need to do an additional count for the total
        if (page != 0 || tasks.size() == size) {
            Long totalCount = taskInfoQueryWrapper.getTaskInfoQuery().count();
            result.setTotal(Long.valueOf(totalCount.intValue()));
            result.setStart(page * size);
        }

        return result;
    }

    protected List<TaskRepresentation> convertTaskInfoList(List<? extends TaskInfo> tasks, Map<String, String> processInstanceNames, Map<String, String> caseInstancesNames) {
        List<TaskRepresentation> result = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(tasks)) {
            for (TaskInfo task : tasks) {

                TaskRepresentation taskRepresentation = null;
                if (task.getScopeDefinitionId() != null) {
                    CaseDefinition caseDefinition = cmmnRepositoryService.getCaseDefinition(task.getScopeDefinitionId());
                    taskRepresentation = new TaskRepresentationVo(task, caseDefinition, caseInstancesNames.get(task.getScopeId()));

                } else {
                    ProcessDefinitionEntity processDefinition = null;
                    if (task.getProcessDefinitionId() != null) {
                        processDefinition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(task.getProcessDefinitionId());
                    }
                    taskRepresentation = new TaskRepresentationVo(task, processDefinition, processInstanceNames.get(task.getProcessInstanceId()));
                }

                if (StringUtils.isNotEmpty(task.getAssignee())) {
                    UserCache.CachedUser cachedUser = userCache.getUser(task.getAssignee());
                    if (cachedUser != null && cachedUser.getUser() != null) {
                        User assignee = cachedUser.getUser();
                        taskRepresentation.setAssignee(new UserRepresentation(assignee));
                    }
                }

                result.add(taskRepresentation);
            }
        }
        return result;
    }


    // 自定义扩展查询字段
    protected void handleExt(TaskInfoQueryWrapper taskInfoQueryWrapper, JsonNode jsonNode) {
        JsonNode businessKey = jsonNode.get("businessKey");
        if (businessKey != null && !businessKey.isNull()) {
            taskInfoQueryWrapper.getTaskInfoQuery().processInstanceBusinessKeyLikeIgnoreCase(StrUtil.format("%{}%", businessKey.asText()));
        }

    }

}
